// End-to-end integration tests for the complete file deletion workflow
// This test simulates the exact web UI behavior to ensure deletions work correctly

use serde_json::Value;
use std::sync::Arc;
use std::time::Duration;
use tempfile::TempDir;
use tokio::time::timeout;

use axum::{
    extract::DefaultBodyLimit,
    http::Method,
    routing::{delete, get, post, put},
    Router,
};
use rdm_rust::config::AppConfig;
use rdm_rust::database::connection::establish_connection_pool;
use rdm_rust::handlers::{auth, data};
use rdm_rust::services::{AuthService, DataService, DvcService, StorageService};
use rdm_rust::AppState;
use reqwest::multipart;
use tower::ServiceBuilder;
use tower_http::{
    cors::{Any, CorsLayer},
    trace::TraceLayer,
};

// Test configuration
const TEST_SERVER_PORT: u16 = 0; // Use random available port
const TEST_TIMEOUT: Duration = Duration::from_secs(30);

struct TestServer {
    port: u16,
    temp_dir: TempDir,
    base_url: String,
    client: reqwest::Client,
}

impl TestServer {
    async fn new() -> Self {
        // Create temporary directory for test storage
        let temp_dir = TempDir::new().expect("Failed to create temp dir");

        // Load test configuration
        let mut config = AppConfig::from_env().expect("Failed to load config");
        config.server.port = TEST_SERVER_PORT;
        config.dvc.storage_path = temp_dir.path().to_string_lossy().to_string();

        // Initialize database
        let db_pool = establish_connection_pool(&config.database.url)
            .await
            .expect("Failed to establish database connection");

        // Initialize services
        let auth_service = Arc::new(AuthService::new(db_pool.clone(), config.clone()));
        let data_service = Arc::new(DataService::new(db_pool.clone()));
        let storage_service = Arc::new(StorageService::new(config.dvc.storage_path.clone()));
        let dvc_service = Arc::new(DvcService::new(db_pool.clone()));

        let app_state = AppState {
            auth_service,
            data_service,
            storage_service,
            dvc_service,
        };

        // Build application router (same as main.rs)
        let public_routes = Router::new()
            .route("/api/register", post(auth::register))
            .route("/api/register-admin", post(auth::register_admin))
            .route("/api/login", post(auth::login))
            .route("/api/data/public", get(data::list_public_data_items));

        let protected_routes = Router::new()
            .route("/api/user-info", get(auth::user_info))
            .route("/api/data", get(data::list_data_items))
            .route("/api/data/upload", post(data::upload_data))
            .route("/api/data/:id", get(data::get_data_item))
            .route("/api/data/:id", delete(data::delete_data_item))
            .route("/api/data/:id/download", get(data::download_data_file))
            .route("/api/data/:id/dvc", get(data::download_dvc_file))
            .route("/api/data/:id/dvc/content", get(data::get_dvc_content))
            .route("/api/data/:id/metadata", put(data::upload_metadata))
            .route_layer(axum::middleware::from_fn_with_state(
                app_state.clone(),
                rdm_rust::middleware::auth::auth_middleware,
            ));

        let app = Router::new()
            .merge(public_routes)
            .merge(protected_routes)
            .layer(
                ServiceBuilder::new()
                    .layer(TraceLayer::new_for_http())
                    .layer(
                        CorsLayer::new()
                            .allow_origin(Any)
                            .allow_methods([
                                Method::GET,
                                Method::POST,
                                Method::PUT,
                                Method::DELETE,
                                Method::OPTIONS,
                            ])
                            .allow_headers(Any),
                    )
                    .layer(DefaultBodyLimit::max(100 * 1024 * 1024)), // 100MB max file size
            )
            .with_state(app_state);

        // Start server on random port
        let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
            .await
            .expect("Failed to bind to address");
        let port = listener
            .local_addr()
            .expect("Failed to get local address")
            .port();

        // Spawn server task
        tokio::spawn(async move {
            axum::serve(listener, app)
                .await
                .expect("Failed to serve server");
        });

        // Give server time to start
        tokio::time::sleep(Duration::from_millis(100)).await;

        let base_url = format!("http://127.0.0.1:{}", port);
        let client = reqwest::Client::builder()
            .timeout(TEST_TIMEOUT)
            .build()
            .expect("Failed to create HTTP client");

        Self {
            port,
            temp_dir,
            base_url,
            client,
        }
    }

    async fn wait_for_server(&self) -> Result<(), Box<dyn std::error::Error>> {
        let health_url = format!("{}/api/data/public", self.base_url);

        for _ in 0..10 {
            match timeout(Duration::from_secs(1), self.client.get(&health_url).send()).await {
                Ok(Ok(response)) if response.status().is_success() => return Ok(()),
                Ok(_) => (),
                Err(_) => (),
            }
            tokio::time::sleep(Duration::from_millis(100)).await;
        }

        Err("Server failed to start".into())
    }

    async fn register_and_login(&self, email: &str, password: &str) -> String {
        // Register user
        let register_response = self
            .client
            .post(format!("{}/api/register", self.base_url))
            .json(&serde_json::json!({
                "email": email,
                "password": password
            }))
            .send()
            .await
            .expect("Failed to send register request");

        assert!(
            register_response.status().is_success(),
            "User registration should succeed"
        );

        // Login to get auth token
        let login_response = self
            .client
            .post(format!("{}/api/login", self.base_url))
            .json(&serde_json::json!({
                "email": email,
                "password": password
            }))
            .send()
            .await
            .expect("Failed to send login request");

        assert_eq!(login_response.status(), 200, "Login should succeed");

        let login_body: Value = login_response
            .json()
            .await
            .expect("Failed to parse login response");
        let auth_token = login_body
            .get("access_token")
            .or_else(|| login_body.get("token"))
            .and_then(|t| t.as_str())
            .expect("Login response should contain token");

        auth_token.to_string()
    }

    async fn upload_test_file(
        &self,
        auth_token: &str,
        filename: &str,
        content: &[u8],
        project: String,
    ) -> i64 {
        let form = multipart::Form::new()
            .part(
                "files",
                multipart::Part::bytes(content.to_vec())
                    .file_name(filename.to_string())
                    .mime_str("text/plain")
                    .expect("Invalid mime type"),
            )
            .text("project", project)
            .text("source", "test://delete-test")
            .text("description", "Test file for delete integration tests");

        let upload_response = self
            .client
            .post(format!("{}/api/data/upload", self.base_url))
            .header("Authorization", format!("Bearer {}", auth_token))
            .multipart(form)
            .send()
            .await
            .expect("Failed to send upload request");

        assert_eq!(upload_response.status(), 200, "File upload should succeed");

        let upload_body: Value = upload_response
            .json()
            .await
            .expect("Failed to parse upload response");
        let uploaded_items = upload_body.get("items").unwrap().as_array().unwrap();
        assert_eq!(uploaded_items.len(), 1);

        let uploaded_item = &uploaded_items[0];
        uploaded_item.get("id").unwrap().as_i64().unwrap()
    }
}

#[tokio::test]
async fn test_end_to_end_delete_workflow() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    println!("Test server running on: {}", server.base_url);

    // Setup: Register and login user
    let test_email = format!(
        "deletetest{}@hillstonenet.com",
        chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
    );
    let test_password = "testpassword123";
    let auth_token = server.register_and_login(&test_email, test_password).await;

    // Setup: Upload test file
    let test_content = b"This is a test file for deletion verification.\nIt contains multiple lines.\nEnd of test.";
    let test_filename = "CTI Workshop Full Slides-Mitre ATT&CK.pdf"; // Simulate the problematic filename
    let item_id = server
        .upload_test_file(
            &auth_token,
            test_filename,
            test_content,
            "Delete Test Project".to_string(),
        )
        .await;

    println!("Uploaded test file: {} (ID: {})", test_filename, item_id);

    // Verify file exists before deletion
    let get_response = server
        .client
        .get(format!("{}/api/data/{}", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to get data item");

    assert_eq!(
        get_response.status(),
        200,
        "File should exist before deletion"
    );

    let get_body: Value = get_response
        .json()
        .await
        .expect("Failed to parse get response");
    assert_eq!(
        get_body.get("name").unwrap().as_str().unwrap(),
        test_filename
    );

    // Test 1: Delete the file
    let delete_response = server
        .client
        .delete(format!("{}/api/data/{}", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to send delete request");

    assert_eq!(
        delete_response.status(),
        204,
        "File deletion should succeed with 204 No Content"
    );

    println!("✓ File deletion test passed");

    // Test 2: Verify file no longer exists
    let get_after_delete_response = server
        .client
        .get(format!("{}/api/data/{}", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to get data item after deletion");

    assert_eq!(
        get_after_delete_response.status(),
        404,
        "File should not exist after deletion"
    );

    // Test 3: Verify file is no longer in the list
    let list_response = server
        .client
        .get(format!("{}/api/data?user_only=true", server.base_url))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to get data list after deletion");

    assert_eq!(list_response.status(), 200, "Data list should succeed");

    let list_body: Value = list_response
        .json()
        .await
        .expect("Failed to parse list response");
    let items = list_body.get("items").unwrap().as_array().unwrap();

    let item_still_exists = items
        .iter()
        .any(|item| item.get("id").unwrap().as_i64().unwrap() == item_id);

    assert!(!item_still_exists, "Deleted item should not be in the list");

    // Test 4: Try to download deleted file (should fail)
    let download_response = server
        .client
        .get(format!("{}/api/data/{}/download", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to send download request for deleted file");

    assert_eq!(
        download_response.status(),
        404,
        "Download of deleted file should return 404"
    );

    // Test 5: Try to get DVC file for deleted file (should fail)
    let dvc_response = server
        .client
        .get(format!("{}/api/data/{}/dvc", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to send DVC request for deleted file");

    assert_eq!(
        dvc_response.status(),
        404,
        "DVC download of deleted file should return 404"
    );

    println!("✓ All deletion verification tests passed");
}

#[tokio::test]
async fn test_delete_nonexistent_file() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    // Setup: Register and login user
    let test_email = format!(
        "nonexistentdelete{}@hillstonenet.com",
        chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
    );
    let test_password = "testpassword123";
    let auth_token = server.register_and_login(&test_email, test_password).await;

    // Test: Try to delete non-existent file (similar to the reported issue with ID 42)
    let nonexistent_id = 42i64; // Using the exact ID from the error message

    let delete_response = server
        .client
        .delete(format!("{}/api/data/{}", server.base_url, nonexistent_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to send delete request for non-existent file");

    assert_eq!(
        delete_response.status(),
        404,
        "Delete of non-existent file should return 404"
    );

    let error_body: Value = delete_response
        .json()
        .await
        .expect("Failed to parse error response");
    assert!(
        error_body
            .get("detail")
            .unwrap()
            .as_str()
            .unwrap()
            .contains("not found")
            || error_body
                .get("detail")
                .unwrap()
                .as_str()
                .unwrap()
                .contains("Data item not found")
    );

    println!("✓ Non-existent file deletion test passed");
}

#[tokio::test]
async fn test_delete_unauthorized_access() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    // Setup: Register two users
    let user1_email = format!(
        "user1delete{}@hillstonenet.com",
        chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
    );
    let user1_password = "password123";
    let user1_token = server
        .register_and_login(&user1_email, user1_password)
        .await;

    let user2_email = format!(
        "user2delete{}@hillstonenet.com",
        chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
    );
    let user2_password = "password123";
    let user2_token = server
        .register_and_login(&user2_email, user2_password)
        .await;

    // User 1 uploads a file
    let test_content = b"Unauthorized deletion test content";
    let item_id = server
        .upload_test_file(
            &user1_token,
            "unauthorized_delete_test.txt",
            test_content,
            "Unauthorized Delete Test".to_string(),
        )
        .await;

    // Test: User 2 tries to delete User 1's file (should fail)
    let unauthorized_delete_response = server
        .client
        .delete(format!("{}/api/data/{}", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", user2_token))
        .send()
        .await
        .expect("Failed to send unauthorized delete request");

    assert_eq!(
        unauthorized_delete_response.status(),
        404,
        "Unauthorized delete should return 404 (item not found for this user)"
    );

    // Verify file still exists for User 1
    let verify_response = server
        .client
        .get(format!("{}/api/data/{}", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", user1_token))
        .send()
        .await
        .expect("Failed to verify file still exists");

    assert_eq!(
        verify_response.status(),
        200,
        "File should still exist for owner"
    );

    println!("✓ Unauthorized deletion test passed");
}

#[tokio::test]
async fn test_delete_without_authentication() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    // Test: Try to delete without authentication
    let delete_response = server
        .client
        .delete(format!("{}/api/data/42", server.base_url)) // Using ID 42 from the error message
        .send()
        .await
        .expect("Failed to send unauthenticated delete request");

    assert_eq!(
        delete_response.status(),
        401,
        "Delete without auth should return 401"
    );

    println!("✓ Unauthenticated deletion test passed");
}

#[tokio::test]
async fn test_delete_multiple_files() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    // Setup: Register and login user
    let test_email = format!(
        "multidelete{}@hillstonenet.com",
        chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
    );
    let test_password = "testpassword123";
    let auth_token = server.register_and_login(&test_email, test_password).await;

    // Upload multiple files
    let file1_content = b"Content of file 1 for deletion";
    let file2_content = b"Content of file 2 for deletion";
    let file3_content = b"Content of file 3 for deletion";

    let item1_id = server
        .upload_test_file(
            &auth_token,
            "delete_file1.txt",
            file1_content,
            "Multi Delete Test".to_string(),
        )
        .await;
    let item2_id = server
        .upload_test_file(
            &auth_token,
            "delete_file2.txt",
            file2_content,
            "Multi Delete Test".to_string(),
        )
        .await;
    let item3_id = server
        .upload_test_file(
            &auth_token,
            "delete_file3.txt",
            file3_content,
            "Multi Delete Test".to_string(),
        )
        .await;

    println!(
        "Uploaded 3 files for deletion: {}, {}, {}",
        item1_id, item2_id, item3_id
    );

    // Verify all files exist before deletion
    let list_before_response = server
        .client
        .get(format!("{}/api/data?user_only=true", server.base_url))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to get data list before deletion");

    let list_before_body: Value = list_before_response
        .json()
        .await
        .expect("Failed to parse list before response");
    let items_before = list_before_body.get("items").unwrap().as_array().unwrap();
    assert_eq!(items_before.len(), 3, "Should have 3 files before deletion");

    // Delete files one by one
    for item_id in [item1_id, item2_id, item3_id] {
        let delete_response = server
            .client
            .delete(format!("{}/api/data/{}", server.base_url, item_id))
            .header("Authorization", format!("Bearer {}", auth_token))
            .send()
            .await
            .expect("Failed to send delete request");

        assert_eq!(
            delete_response.status(),
            204,
            "Each file deletion should succeed"
        );
    }

    // Verify all files are deleted
    let list_after_response = server
        .client
        .get(format!("{}/api/data?user_only=true", server.base_url))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to get data list after deletion");

    let list_after_body: Value = list_after_response
        .json()
        .await
        .expect("Failed to parse list after response");
    let items_after = list_after_body.get("items").unwrap().as_array().unwrap();
    assert_eq!(items_after.len(), 0, "Should have 0 files after deletion");

    println!("✓ Multiple files deletion test passed");
}

#[tokio::test]
async fn test_delete_file_with_special_characters() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    // Setup: Register and login user
    let test_email = format!(
        "specialcharsdelete{}@hillstonenet.com",
        chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
    );
    let test_password = "testpassword123";
    let auth_token = server.register_and_login(&test_email, test_password).await;

    // Test files with special characters (similar to the reported issue)
    let test_cases = vec![
        (
            "CTI Workshop Full Slides-Mitre ATT&CK.pdf",
            b"PDF content with special chars".to_vec(),
        ),
        ("file with spaces.txt", b"Content with spaces".to_vec()),
        ("file-with-dashes.txt", b"Content with dashes".to_vec()),
        (
            "file_with_underscores.txt",
            b"Content with underscores".to_vec(),
        ),
        ("file.with.dots.txt", b"Content with dots".to_vec()),
    ];

    for (filename, content) in test_cases {
        let item_id = server
            .upload_test_file(
                &auth_token,
                filename,
                &content,
                "Special Chars Test".to_string(),
            )
            .await;

        // Verify file exists
        let get_response = server
            .client
            .get(format!("{}/api/data/{}", server.base_url, item_id))
            .header("Authorization", format!("Bearer {}", auth_token))
            .send()
            .await
            .expect("Failed to get data item");

        assert_eq!(
            get_response.status(),
            200,
            "File with special chars should exist"
        );

        // Delete file
        let delete_response = server
            .client
            .delete(format!("{}/api/data/{}", server.base_url, item_id))
            .header("Authorization", format!("Bearer {}", auth_token))
            .send()
            .await
            .expect("Failed to delete file with special chars");

        assert_eq!(
            delete_response.status(),
            204,
            "File with special chars should be deletable"
        );

        // Verify file is deleted
        let get_after_delete_response = server
            .client
            .get(format!("{}/api/data/{}", server.base_url, item_id))
            .header("Authorization", format!("Bearer {}", auth_token))
            .send()
            .await
            .expect("Failed to get data item after deletion");

        assert_eq!(
            get_after_delete_response.status(),
            404,
            "File with special chars should be deleted"
        );
    }

    println!("✓ Special characters deletion test passed");
}

#[tokio::test]
async fn test_delete_and_verify_storage_cleanup() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    // Setup: Register and login user
    let test_email = format!(
        "storagecleanup{}@hillstonenet.com",
        chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
    );
    let test_password = "testpassword123";
    let auth_token = server.register_and_login(&test_email, test_password).await;

    // Upload a test file
    let test_content = b"Storage cleanup test content";
    let test_filename = "storage_cleanup_test.txt";
    let item_id = server
        .upload_test_file(
            &auth_token,
            test_filename,
            test_content,
            "Storage Cleanup Test".to_string(),
        )
        .await;

    // Download the file to verify it exists in storage
    let download_response = server
        .client
        .get(format!("{}/api/data/{}/download", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to download file before deletion");

    assert_eq!(
        download_response.status(),
        200,
        "File should be downloadable before deletion"
    );
    let downloaded_content = download_response
        .bytes()
        .await
        .expect("Failed to read downloaded content");
    assert_eq!(
        downloaded_content.as_ref(),
        test_content,
        "Downloaded content should match original"
    );

    // Delete the file
    let delete_response = server
        .client
        .delete(format!("{}/api/data/{}", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to delete file");

    assert_eq!(
        delete_response.status(),
        204,
        "File deletion should succeed"
    );

    // Try to download the deleted file (should fail)
    let download_after_delete_response = server
        .client
        .get(format!("{}/api/data/{}/download", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to download file after deletion");

    assert_eq!(
        download_after_delete_response.status(),
        404,
        "Download should fail after deletion"
    );

    println!("✓ Storage cleanup test passed");
}
